package com.xiyuan.smartutils.reflect;

import com.xiyuan.smartutils.Asserts;
import com.xiyuan.smartutils.Classes;
import com.xiyuan.smartutils.Systems;
import com.xiyuan.smartutils.exceptions.UtilException;
import com.xiyuan.smartutils.extend.ConcurrentHashSet;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Set;

/**
 * the description for class Reflect
 *
 * @author xiyuan-lgz 2025-03-13 @version v1.0.0 新建与整理
 */

@SuppressWarnings("All")
public class Reflect {
    /**
     * 构造对象缓存
     */
    static Unsafe UNSAFE = Unsafe.getUnsafe();
    
    public static void setBoolean(Object obj, long offset, boolean value) {
        UNSAFE.putBoolean(obj, offset, value);
    }
    
    public static void setBoolean(Object obj, long offset, Boolean value) {
        UNSAFE.putObject(obj, offset, value);
    }
    
    public static boolean getBoolean(Object obj, long offset) {
        return UNSAFE.getBoolean(obj, offset);
    }
    
    public static long getLong(Object obj, long offset) {
        return UNSAFE.getLong(obj, offset);
    }
    
    public static void setLong(Object obj, long offset, long value) {
        UNSAFE.putLong(obj, offset, value);
    }
    
    public static void setLong(Object obj, long offset, Long value) {
        UNSAFE.putLong(obj, offset, value);
    }
    
    public static void setInt(Object obj, long offset, int value) {
        UNSAFE.putInt(obj, offset, value);
    }
    
    public static void setInt(Object obj, long offset, Integer value) {
        UNSAFE.putObject(obj, offset, value);
    }
    
    public static int getInt(Object obj, long offset) {
        return UNSAFE.getInt(obj, offset);
    }
    
    
    public static void setByte(Object obj, long offset, byte value) {
        UNSAFE.putByte(obj, offset, value);
    }
    
    public static void setByte(Object obj, long offset, Byte value) {
        UNSAFE.putObject(obj, offset, value);
    }
    
    public static byte getByte(Object obj, long offset) {
        return UNSAFE.getByte(obj, offset);
    }
    
    public static void setChar(Object obj, long offset, char value) {
        UNSAFE.putChar(obj, offset, value);
    }
    
    public static void setChar(Object obj, long offset, Character value) {
        UNSAFE.putObject(obj, offset, value);
    }
    
    public static char getChar(Object obj, long offset) {
        return UNSAFE.getChar(obj, offset);
    }
    
    public static void setShort(Object obj, long offset, short value) {
        UNSAFE.putShort(obj, offset, value);
    }
    
    public static void setShort(Object obj, long offset, Short value) {
        UNSAFE.putObject(obj, offset, value);
    }
    
    public static short getShort(Object obj, long offset) {
        return UNSAFE.getShort(obj, offset);
    }
    
    public static void setDouble(Object obj, long offset, double value) {
        UNSAFE.putDouble(obj, offset, value);
    }
    
    public static void setDouble(Object obj, long offset, Double value) {
        UNSAFE.putObject(obj, offset, value);
    }
    
    public static double getDouble(Object obj, long offset) {
        return UNSAFE.getDouble(obj, offset);
    }
    
    public static void setFloat(Object obj, long offset, float value) {
        UNSAFE.putFloat(obj, offset, value);
    }
    
    public static void setFloat(Object obj, long offset, Float value) {
        UNSAFE.putObject(obj, offset, value);
    }
    
    public static float getFloat(Object obj, long offset) {
        return UNSAFE.getFloat(obj, offset);
    }
    
    
    public static long getOffset(Field field) {
        return UNSAFE.objectFieldOffset(field);
    }
    
    
    public static void setObjectValue(Object obj, long offset, Object value) {
        UNSAFE.putObject(obj, offset, value);
    }
    
    public static Object getObjectValue(Object obj, long offset) {
        return UNSAFE.getObject(obj, offset);
    }
    
    public static Method setAccessible(Method method) {
        JavaUnsafe.setAccessible(method, true);
        return method;
    }
    
    public static Field setAccessible(Field field) {
        JavaUnsafe.setAccessible(field, true);
        return field;
    }
    
    public static void set(Object obj, Field field, Object value) throws IllegalAccessException {
        field.set(obj, value);
    }
    
    public static void set(Object obj, Field field, boolean value) throws IllegalAccessException {
        field.setBoolean(obj, value);
    }
    
    public static void set(Object obj, Field field, byte value) throws IllegalAccessException {
        field.setByte(obj, value);
    }
    
    public static void set(Object obj, Field field, char value) throws IllegalAccessException {
        field.setChar(obj, value);
    }
    
    public static void set(Object obj, Field field, short value) throws IllegalAccessException {
        field.setShort(obj, value);
    }
    
    public static void set(Object obj, Field field, int value) throws IllegalAccessException {
        field.setInt(obj, value);
    }
    
    public static void set(Object obj, Field field, long value) throws IllegalAccessException {
        field.setLong(obj, value);
    }
    
    public static void set(Object obj, Field field, float value) throws IllegalAccessException {
        field.setFloat(obj, value);
    }
    
    public static void set(Object obj, Field field, double value) throws IllegalAccessException {
        field.setDouble(obj, value);
    }
    
    public static <T> T get(Object obj, Field field) throws IllegalAccessException {
        return (T) field.get(obj);
    }
    
    public static boolean getBoolean(Object obj, Field field) throws IllegalAccessException {
        return field.getBoolean(obj);
    }
    
    public static byte getByte(Object obj, Field field) throws IllegalAccessException {
        return field.getByte(obj);
    }
    
    public static char getChar(Object obj, Field field) throws IllegalAccessException {
        return field.getChar(obj);
    }
    
    public static short getShort(Object obj, Field field) throws IllegalAccessException {
        return field.getShort(obj);
    }
    
    public static int getInt(Object obj, Field field) throws IllegalAccessException {
        return field.getInt(obj);
    }
    
    public static long getLong(Object obj, Field field) throws IllegalAccessException {
        return field.getLong(obj);
    }
    
    public static float getFloat(Object obj, Field field) throws IllegalAccessException {
        return field.getFloat(obj);
    }
    
    public static double getDouble(Object obj, Field field) throws IllegalAccessException {
        return field.getDouble(obj);
    }
    
    // --------------------------------------------------------------------------------------------------------- newInstance
    
    /**
     * 实例化对象
     *
     * @param <T>   对象类型
     * @param clazz 类名
     * @return 对象
     * @throws UtilException 包装各类异常
     */
    @SuppressWarnings("unchecked")
    public static <T> T newInstance(String clazz) throws UtilException {
        try {
            return (T) Class.forName(clazz).newInstance();
        } catch (Exception e) {
            throw new UtilException(e, "Instance class [%s] error!", clazz);
        }
    }
    
    /**
     * 实例化对象
     *
     * @param <T>    对象类型
     * @param clazz  类
     * @param params 构造函数参数
     * @return 对象
     * @throws UtilException 包装各类异常
     */
    public static <T> T newInstance(Class<T> clazz, Object... params) throws UtilException {
        if (params == null || params.length == 0) {
            final Constructor<T> constructor = getConstructor(clazz);
            if (null == constructor) {
                throw new UtilException("No constructor for [%s]", clazz);
            }
            try {
                return constructor.newInstance();
            } catch (Exception e) {
                throw new UtilException(e, "Instance class [%s] error!", clazz);
            }
        }
        
        final Class<?>[] paramTypes = Classes.getClasses(params);
        final Constructor<T> constructor = getConstructor(clazz, paramTypes);
        if (null == constructor) {
            throw new UtilException("No Constructor matched for parameter types: [{}]", new Object[]{paramTypes});
        }
        try {
            return constructor.newInstance(params);
        } catch (Exception e) {
            throw new UtilException(e, "Instance class [{}] error!", clazz);
        }
    }
    
    // --------------------------------------------------------------------------------------------------------- Constructor
    
    /**
     * 查找类中的指定参数的构造方法，如果找到构造方法，会自动设置可访问为true
     *
     * @param <T>            对象类型
     * @param clazz          类
     * @param parameterTypes 参数类型，只要任何一个参数是指定参数的父类或接口或相等即可，此参数可以不传
     * @return 构造方法，如果未找到返回null
     */
    @SuppressWarnings("unchecked")
    public static <T> Constructor<T> getConstructor(Class<T> clazz, Class<?>... parameterTypes) {
        if (null == clazz) {
            return null;
        }
        
        final Constructor<?>[] constructors = getConstructors(clazz);
        Class<?>[] pts;
        for (Constructor<?> constructor : constructors) {
            pts = constructor.getParameterTypes();
            if (Classes.isAllAssignableFrom(pts, parameterTypes)) {
                // 构造可访问
                UNSAFE.setAccessible( constructor);
                return (Constructor<T>) constructor;
            }
        }
        return null;
    }
    
    /**
     * 获得一个类中所有构造列表
     *
     * @param <T>       构造的对象类型
     * @param beanClass 类，非{@code null}
     * @return 字段列表
     * @throws SecurityException 安全检查异常
     */
    @SuppressWarnings("unchecked")
    public static <T> Constructor<T>[] getConstructors(Class<T> beanClass) throws SecurityException {
        if(beanClass == null)return new Constructor[0];
        return (Constructor<T>[])beanClass.getDeclaredConstructors();
    }
 
//
//    private static void forceMakeAccessible(AccessibleObject obj) throws Exception {
//
//        // 绕过 JDK 16+ 的 canAccess 检查
//        Class<?> accessClass = Class.forName("jdk.internal.access.SharedSecrets");
//        Object access = accessClass.getMethod("getJavaLangAccess").invoke(null);
//        Method getModuleAccess = accessClass.getMethod("getModuleAccess");
//        Object moduleAccess = getModuleAccess.invoke(access);
//        Method makeAccessible = moduleAccess.getClass().getMethod("makeAccessible", AccessibleObject.class);
//        makeAccessible.invoke(moduleAccess, obj);
//    }
}
